#!/usr/bin/env python

###########################################################################
##   This program is free software; you can redistribute it and/or modify #
##   it under the terms of the GNU General Public License as published by #
##   the Free Software Foundation, version 2.
##
##   This program is distributed in the hope that it will be useful,
##   but WITHOUT ANY WARRANTY; without even the implied warranty of
##   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
##   GNU General Public License for more details.
##
##   You have received a copy of the GNU General Public License along
##   with this program, on the COPYING file.

#Objective:
# Create a beamer presentation from a txt file with minimal markup.

#Uses txt2tags (http://txt2tags.sourceforge.net/) to convert the txt
#file to tex. The markup follows the same format as txt2tags, with
#the third level of heading (\subsubsection) starting each slide. A small
#modification is a commented out section after the header info which 
#allows the user to provide a longer title (which may span multiple lines),
#along with a short title, subtitle, list of authors with a short form, etc,
#which I find useful. (see accompanying template file). 
 
#The tex file is modified to generate a beamer latex document
#It would have been preferable to use txt2tags to do the whole thing, 
#but slides need to be enclosed in \begin{frame} and \end{frame} and I 
#dont think it is possible to get \end{frame} working.
#hackish, but works !!

#Author:
      # Raja Selvaraj
      # rajajs (at) gmail (dot) com

#Usage:
      # python txt2beamer.py textfile.t2t
      
#########################################################################

## imports
import re , sys, os
from optparse import OptionParser
import txt2tags

## constants
SUBSUB  	= re.compile('subsubsection\*')  # re expression for subsubsection tag
FRAMEBEGIN 	= 'begin{frame}'                 # framebegin tag
FRAMEEND 	= '\end{frame}'                  # frameend tag 

LINESEP = '\n'# Dont use os.linesep as it results in two new lines on windows

INFILES_CONFIG_DICT_ENTRIES = [('outfile' , '-module-'),
                               ('infile'  , ['']      ),
                               ('target'  , 'tex'     ),
                               ('enum-title','n')]

DEFAULT_HEADER = """
\documentclass{beamer}
\mode<presentation>{
  \usetheme{Warsaw}
  \setbeamercovered{transparent}
  }
\usepackage[english]{babel}
\usepackage[latin1]{inputenc}
\usepackage{times}
\usepackage[T1]{fontenc}

"""

## functions

def first_empty_line(lineindex, lines):
	"""
	Find the first of the preceding empty lines from the given index
	to locate the position to insert an end frame tag
	"""
	while len(lines[lineindex].strip()) != 0:
		lineindex -= 1
	return lineindex

def ins_begin_frame(inputstring):
	"""
	In the inputstring, replace the subsubsection title tags with
	frame begin tags
	"""
	return re.sub(SUBSUB, FRAMEBEGIN, inputstring)


def ins_end_frame(inputstring):
	"""
	Scan the inputstring line by line to find the location for end of 
	frame and insert the end frame tag.
	endframe is inserted when there is a preceding unmatched begin frame
	and the beginning of a new frame / section / subsection or end of 
	document is encountered.
	End frame tag is inserted after the last non-empty line to produce
	clean latex.
	"""
	endexpected = False    # is a begin frame waiting to be matched? 
	currentlineindex = 0
		
	lines = inputstring.split(LINESEP) 
	while currentlineindex < len(lines):
			
		framebegins   =  sectionbegins =  documentends  =  False
		currentline = lines[currentlineindex].strip()
			
		# classify current line
		if currentline.startswith('\\begin{frame}'):
			framebegins = True
		
		if currentline.startswith('\\section') or\
		   currentline.startswith('\\subsection'):
			sectionbegins = True
			
		if currentline.startswith('\\end{document}'):
			documentends = True
		
		## main
        #######
        
        # lets begin a new frame
		if framebegins and not endexpected:
			endexpected = True
		
        # catch frame end
		elif endexpected:
			if framebegins:
				lines.insert(first_empty_line(currentlineindex, lines), FRAMEEND)
				currentlineindex += 1
			elif sectionbegins or documentends:
				endexpected = False
				lines.insert(first_empty_line(currentlineindex, lines), FRAMEEND)
				currentlineindex += 1
			else:
				pass
				
		else:
			pass
				
		currentlineindex += 1	
	
	return LINESEP.join(lines)    


def get_extra_info(inputstring):
	"""
	Obtain the extra information in the t2t file and enter as a 
	dictionary. The 'extra info' is written as commented text between 
	two horizontal lines drawn with '-' after the header section.
	(See accompanying template file)
	"""
        #extrainfo = []
	# get info between the lines
	delimline = re.compile('%-+')
	extrainfo = re.split(delimline, inputstring)[1]
	
	# enter into a dict
	infodict = {}  # TODO: ?redefining from outer scope?
	infoparts = re.sub(']', '[', extrainfo).split('[') #split at brackets
	infoparts = [re.sub('\n%', '', x).strip() for x in infoparts] #remove '\n%' #TODO: what about \r\n?
	
	for i in range(0, len(infoparts)-1, 2):
		infodict[infoparts[i]] = infoparts[i+1]
		
	infodict['short subtitle'] = ''
	infodict['short date'] = ''
	
	return infodict
	

## Modify header information
def make_header(infodict):
	"""
	Make the header files for a beamer presentation using some
	default header lines and then the full title, subtitle,etc
	"""
	# start with the default lines
	header = DEFAULT_HEADER

             
	# add the extra info
	for tag in ['title', 'subtitle', 'author', 'date']:

		header += ''.join(['\\', tag, 
		                   '[', infodict.get('short '+tag),']', LINESEP,
		                   '{', infodict.get(tag), '}', LINESEP])
	
	# add the title slide
        header +=  LINESEP+ "\\begin{document}" \
                  +LINESEP+ "\\begin{frame}"   \
	          +LINESEP+ "\\titlepage"       \
	          +LINESEP+ "\\end{frame}"  + LINESEP
	
	return header
	
def get_options():
	"""
	Read the options. Only infile is a required option
	Options are specified as arguments, or in their absence, by a 
	commented section in the file reserved for options. 
	""" 
	parser = OptionParser()
        parser.add_option('-i', '--infile', dest="infile",
                          help = 'full path to the text file')
        parser.add_option('-o', '--outfile', dest="outfile", default = '',
                          help = 'path for the output file')
        parser.add_option('-t', '--title', dest="title", default = '',
                          help = 'full title for the presentation')
        parser.add_option('-s', '--short', dest="short_title", default = '',
                          help = 'short title for the presentation')
        parser.add_option('-a', '--author', dest='author', default = '',
                          help = 'author names separated by commas')
	
        (options, args) = parser.parse_args()
        
        return options

if __name__  ==  '__main__':
	
	options = get_options()
        
	#INFILES = []
	#INFILES.append(sys.argv[1])
        INFILES = [options.infile]
		
	# generate infiles_config
	INFILES_CONFIG = txt2tags.get_infiles_config(INFILES)
        print INFILES_CONFIG[0][0]	
	# change the information in config_dict_entries so that we dont 
	# bump into errors later  #TODO: Clarify what I am doing
	for entry in INFILES_CONFIG_DICT_ENTRIES:
		INFILES_CONFIG[0][0][entry[0]] = entry[1]
		
	# with outfile set as -module-, the converted file is returned as list
	TEXFORMATTED = LINESEP.join(txt2tags.convert_this_files(INFILES_CONFIG)[0])
                
	# insert begin frame and end frame	
	BEAMERFORMATTED = ins_end_frame(ins_begin_frame(TEXFORMATTED))
        
                
	# construct header
	INFODICT = get_extra_info(open(INFILES[0],'r').read())
        #INFODICT = dict()
	HEADER = make_header(INFODICT)
	BEAMER = HEADER + LINESEP.join(BEAMERFORMATTED.split(LINESEP)[9:])
        
	
	fi = open('test_beamer.tex','w')
	fi.write(BEAMER)
	fi.close()
